package com.mzjf.boot.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.lang.reflect.Method;

/**
 * redis配置 
 * @author  wuhongpu
 * @create 2018-06-19 下午9:38
 **/  
@Configuration
//@EnableCaching//开启缓存
public class RedisConfiguration extends CachingConfigurerSupport {
    private static final Log log = LogFactory.getLog(RedisConfiguration.class);
    @Value("${jedis.pool.host}")
    private String host;  
    @Value("${jedis.pool.port}")
    private Integer port;
    @Value("${jedis.pool.database}")
    private Integer database;
    @Value("${jedis.pool.password}")
    private String password ;

    @Value("${jedis.pool.timeout}")
    private Integer timeout;

    @Value("${jedis.pool.config.maxTotal}")
    private Integer maxTotal;  
    @Value("${jedis.pool.config.maxIdle}")
    private Integer maxIdle;  
    @Value("${jedis.pool.config.maxWaitMillis}")
    private Long maxWaitMillis;  
    @Value("${jedis.pool.testOnBorrow}")
    private Boolean testOnBorrow;

  
  
    /** 
     * 注入 RedisConnectionFactory 
     */  
    @Autowired
    RedisConnectionFactory redisConnectionFactory;
  
    /** 
     * 缓存管理器. 
     * 
     * @param redisTemplate 
     * @return 
     */

    @Bean
    public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
        RedisCacheManager  cacheManager = new RedisCacheManager(redisTemplate);
        //设置缓存过期时间
       // cacheManager.setDefaultExpiration(60);//秒
        return cacheManager;  
    }

//    @Bean
//    public CacheManager cacheManager(RedisTemplate redisTemplate) {
//        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//        //设置缓存过期时间
//        rcm.setDefaultExpiration(60);//秒
//        return rcm;
//    }

    /**
     *  注解@Cache key生成规则
     *  此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样，key也会不一样。
     *  自定义key生成策略，防止Long类型会出现异常信息
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /** 
     * 配置redisTemplate 
     * <p> 
     * 通过redisConnectionFactory引入redis在配置文件中的连接配置 
     */  
    @Bean  
    public RedisTemplate<String, Object> redisTemplate() {
        log.info("初始化RedisTemplate22222222222");
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();  
        initDomainRedisTemplate(redisTemplate, redisConnectionFactory);  
        redisTemplate.afterPropertiesSet();  
        return redisTemplate;  
    }  
  
    @Bean  
    public RedisConnectionFactory redisConnectionFactory() {
        log.info("初始化RedisConnectionFactory");
        JedisPoolConfig jedisPoolConfig = getJedisPoolConfig();
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName(host);  
        jedisConnectionFactory.setPort(port);  
        jedisConnectionFactory.setDatabase(database);
        jedisConnectionFactory.setTimeout(timeout);
        jedisConnectionFactory.setUsePool(true);
        jedisConnectionFactory.setPassword(password);
        jedisConnectionFactory.setPoolConfig(jedisPoolConfig);  
        return jedisConnectionFactory;  
    }  
  
    private JedisPoolConfig getJedisPoolConfig() {
        log.info("初始化JedisPoolConfig");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();  
        //控制一个pool可分配多少个jedis实例，通过pool.getResource()来获取；  
        //如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。  
        jedisPoolConfig.setMaxTotal(maxTotal);  
        //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。  
        jedisPoolConfig.setMaxIdle(maxIdle);  
        //表示当borrow(引入)一个jedis实例时，最大的等待时间，如果超过等待时间，则直接抛出JedisConnectionException；  
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);  
        //在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；  
        //jedisPoolConfig.setTestOnBorrow(testOnBorrow);
       // jedisPoolConfig.setTestOnCreate(true);
        return jedisPoolConfig;  
    }  
  
    /** 
     * 设置数据存入 redis 的序列化方式 
     * 
     * @param redisTemplate 
     * @param factory 
     */  
    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
        log.info("初始化RedisTemplate111111111111");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());  
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());  
        redisTemplate.setConnectionFactory(factory);  
    }


    /**
     * redis数据操作异常处理
     * 这里的处理：在日志中打印出错误信息，但是放行
     * 保证redis服务器出现连接等问题的时候不影响程序的正常运行，使得能够出问题时不用缓存
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.error("redis异常：key=[{}]"+key,e);
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.error("redis异常：key=[{}]"+key,e);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key)    {
                log.error("redis异常：key=[{}]"+key,e);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.error("redis异常：",e);
            }
        };
        return cacheErrorHandler;
    }

}
